﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using NUnit.Framework;
using PropertyExpression.ControllableQuery;
using PropertyExpression.ControllableQuery.Test;
using PropertyExpression.ControllableQuery.TypedReader;
using PropertyExpression.Common;

namespace PropertyExpression.PwsApplication
{
    [TestFixture]
    public class MainTest
    {
        [Test]
        public void CreateSellerTest()
        {
            ExecuteNonQuery(context => new ClearDatabaseQuery());

            ExecuteNonQuery(context => new InsertSellerQuery(context.CreateParam(Guid.NewGuid())));
        }

        [Test]
        public void CreateBuyerTest()
        {
            ExecuteNonQuery(context => new ClearDatabaseQuery());

            ExecuteNonQuery(context => new InsertBuyerQuery(context.CreateParam(Guid.NewGuid())));
        }

        [Test]
        public void NewQuotation()
        {
            ExecuteNonQuery(context => new ClearDatabaseQuery());

            var sellerID = Guid.NewGuid();
            ExecuteNonQuery(context => new InsertSellerQuery(context.CreateParam(sellerID)));

            ExecuteNonQuery(context => new InsertQuotationHeaderQuery(
                                           context.CreateParam(Guid.NewGuid()),
                                           context.CreateParam(sellerID.AsOptionalValue()),
                                           context.CreateParam(OptionalValue.Nothing<Guid>())));
        }

        public static IEnumerable<IDataReader<T>> ExecuteReader<T>(Func<QueryContext, IQuery<T>> queryFunc)
        {
            return WithSqlConnection(connection => WithSqlCommand(queryFunc, ExecuteReader, connection));
        }

        public static int ExecuteNonQuery(Func<QueryContext, IQuery> queryFunc)
        {
            return WithSqlConnection(connection => WithSqlCommand(queryFunc, (query, sqlCommand) => new[] { sqlCommand.ExecuteNonQuery() }, connection)).Single();
        }

        private static IEnumerable<IDataReader<T>> ExecuteReader<T>(IQuery<T> query, SqlCommand sqlCommand)
        {
            using (var sqlDataReader = sqlCommand.ExecuteReader())
            {
                while (sqlDataReader.Read())
                {
                    yield return query.GetReader(sqlDataReader);
                }
            }
        }

        private static IEnumerable<TResult> WithSqlConnection<TResult>(
            Func<SqlConnection, IEnumerable<TResult>> func)
        {
            using (var connection = new SqlConnection(ConnectionString))
            {
                connection.Open();

                foreach (var result in func(connection)) yield return result;
            }
        }

        private static IEnumerable<TResult> WithSqlCommand<TQuery, TResult>(
            Func<QueryContext, TQuery> queryFunc,
            Func<TQuery, SqlCommand, IEnumerable<TResult>> func,
            SqlConnection connection) where TQuery : IQuery
        {
            using (var sqlCommand = new SqlCommand())
            {
                sqlCommand.Connection = connection;

                var query = queryFunc(new QueryContext(sqlCommand));

                sqlCommand.CommandText = query.TransformText();

                foreach (var result in func(query, sqlCommand)) yield return result;
            }
        }

        internal static string ConnectionString
        {
            get { return @"Data Source=(local);Initial Catalog=Pws;Integrated Security=True"; }
        }
    }
}
